#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <directfb.h>
static IDirectFB *dfb = NULL;
static IDirectFBSurface *primary = NULL;
#define DFBCHECK(x...)                                         \
  {                                                            \
    DFBResult err = x;                                         \
                                                               \
    if (err != DFB_OK)                                         \
      {                                                        \
        fprintf( stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \
        DirectFBErrorFatal( #x, err );                         \
      }                                                        \
  }
int screen_width = 0, screen_height = 0;
const char *filename = NULL;
static void *streaming_thread(void *arg)
{
     DFBResult            ret;
     int                  fd;
     unsigned int         len   = 0;
     int                  total = 0;
     char                 data[8192];
     struct stat          stat;
     DFBRectangle         rect;
     IDirectFBSurface    *progress;
     IDirectFBDataBuffer *buffer = (IDirectFBDataBuffer *) arg;
     
     fd = open( filename, O_RDONLY );
     if (fd < 0) {
          perror( "open" );
          return NULL;
     }

     if (fstat( fd, &stat ) < 0) {
          perror( "fstat" );
          close( fd );
          return NULL;
     }

     rect.x = 0;
     rect.y = screen_height - 10;
     rect.w = screen_width;
     rect.h = 5;

     DFBCHECK(primary->GetSubSurface( primary, &rect, &progress ));

     progress->SetColor( progress, 0, 0, 0xff, 0xff );
     
     primary->DrawString( primary, "Thread running, streaming data...",
                          -1, 10, 40, DSTF_TOPLEFT );
     
     while (1) {
          char msg[32];

          pthread_testcancel();

          usleep( ((rand()%1000000) + 2000000) / (stat.st_size >> 10) );
         
          /* actually limit the amount of data in buffer to 64 Kb */
          DFBCHECK(buffer->GetLength( buffer, &len ));
          if (len >= 64*1024)
               continue;

          /* put some data with variing length */
          len = read( fd, data, (rand()%8192) + 1 );
          if (len <= 0) {
               /* End-of-File */
               DFBCHECK(buffer->Finish( buffer ));
               break;
          }
          
          DFBCHECK(buffer->PutData( buffer, data, len ));

          total += len;
          

          DFBCHECK(buffer->GetLength( buffer, &len ));

          snprintf( msg, 32, "Bytes in buffer: %d", len );
          
          primary->SetColor( primary, 0, 0, 0, 0 );
          primary->FillRectangle( primary, 40, 80, 200, 25 );

          primary->SetColor( primary, 0xdd, 0xdd, 0xdd, 0xff );
          primary->DrawString( primary, msg, -1, 40, 80, DSTF_TOPLEFT );
          
          progress->FillRectangle( progress, 0, 0,
                                   total * screen_width / stat.st_size, 5 );
     }

     progress->Release( progress );

     close( fd );

     return NULL;
}
DIRenderCallbackResult render_callback( DFBRectangle *rect, void *ctx )
{
     int               width;
     int               height;
     IDirectFBSurface *image = (IDirectFBSurface*) ctx;

     image->GetSize( image, &width, &height );

     primary->Blit( primary, image, rect,
                    (screen_width - width) / 2 + rect->x,
                    (screen_height - height) / 2 + rect->y);

     return DIRCR_OK;
}
void frame_callback( void *ctx )
{
     int               width;
     int               height;
     IDirectFBSurface *image = (IDirectFBSurface*) ctx;

     image->GetSize( image, &width, &height );
     
     primary->Blit( primary, image, NULL, 
                    (screen_width - width) / 2,
                    (screen_height - height) / 2);
}
void test_file_streamed()
{
     DFBResult                 ret;
     DFBSurfaceDescription     sdsc;
     IDirectFBDataBuffer      *buffer;
     IDirectFBImageProvider   *image_provider = NULL;
     IDirectFBVideoProvider   *video_provider = NULL;
     IDirectFBSurface         *image;
     pthread_t                 st;
     
     if (access( filename, R_OK ) < 0) {
          perror( "access" );
          return;
     }          
     
     primary->Clear( primary, 0, 0, 0, 0 );
     primary->SetColor( primary, 0xcc, 0xcc, 0xcc, 0xff );

     //create a streamed data buffer 
     DFBCHECK(dfb->CreateDataBuffer( dfb, NULL, &buffer ));

     primary->DrawString( primary, "Databuffer created, starting thread...",
                          -1, 10, 10, DSTF_TOPLEFT );
     // create thread that will feed the buffer 
     pthread_create( &st, NULL, streaming_thread, buffer );
     
     ret = buffer->CreateImageProvider( buffer, &image_provider );
     if (ret == DFB_OK) {
          DFBCHECK(image_provider->GetSurfaceDescription( image_provider, &sdsc ));
     }
     else {
          DirectFBError( "IDirectFBDataBuffer::CreateImageProvider()", ret );

          DFBCHECK(buffer->CreateVideoProvider( buffer, &video_provider ));
               
          DFBCHECK(video_provider->GetSurfaceDescription( video_provider, &sdsc ));
     }

     printf( "\nImage size: %dx%d\n\n", sdsc.width, sdsc.height );

     DFBCHECK(dfb->CreateSurface( dfb, &sdsc, &image ));

     if (image_provider) {
          image_provider->SetRenderCallback( image_provider, render_callback, image );
     
          DFBCHECK(image_provider->RenderTo( image_provider, image, NULL ));
     } // video_provider 
     else { 
          DFBCHECK(video_provider->PlayTo( video_provider, image, NULL,
                                                  frame_callback, image ));

          // play 10 seconds 
          sleep( 10 );

          video_provider->Stop( video_provider );
     }

     pthread_cancel( st );
     pthread_join( st, NULL );
     
     primary->Blit( primary, image, NULL,
                    (screen_width - (int)sdsc.width) / 2,
                    (screen_height - (int)sdsc.height) / 2);
     
     image->Release( image );
     if (image_provider)
          image_provider->Release( image_provider );
     if (video_provider)
          video_provider->Release( video_provider );
     buffer->Release( buffer );
     sleep( 2 );
}
int main(int argc, char **argv)
{
	/* check arguments */
	if (argc < 2) {
		fprintf( stderr, "\nUsage: <filename>\n\n" );
		exit(1);
	}
	/* use this file */
	filename = argv[1];
	DFBSurfaceDescription dsc;
	DFBCHECK (DirectFBInit (&argc, &argv));
	DFBCHECK (DirectFBCreate (&dfb));
	DFBCHECK (dfb->SetCooperativeLevel (dfb, DFSCL_FULLSCREEN));
	dsc.flags = DSDESC_CAPS;
	dsc.caps  = DSCAPS_PRIMARY;// | DSCAPS_FLIPPING
	DFBCHECK (dfb->CreateSurface( dfb, &dsc, &primary ));
	DFBCHECK (primary->GetSize (primary, &screen_width, &screen_height));
	test_file_streamed();

	sleep (5);
	primary->Release( primary );
	dfb->Release( dfb );
	return 23;
}
